﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Epsilon.Expr;

namespace Epsilon.Parse
{
    public partial class Parser
    {
        public List<SecLabel> ParseSecLabels()
        {
            List<SecLabel> labels = new List<SecLabel>();
            labels.Add(ParseSecLabel());
            while (MatchPeek(TokenType.Symbol, ","))
            {
                Chomp();
                labels.Add(ParseSecLabel());
            }
            return labels;
        }

        public SecLabel ParseSecLabel()
        {
            if (MatchPeek(TokenType.Symbol, "("))
            {
                Match(TokenType.Symbol, "(");
                SecLabel label = ParseSecLabel();
                Match(TokenType.Symbol, ")");
                return label;
            }
            else if (MatchPeek(TokenType.Symbol, "{"))
            {
                Match(TokenType.Symbol, "{");
                List<SecLabel> labels = ParseSecLabels();
                Match(TokenType.Symbol, "}");
                return new SecTuple(labels);
            }
            else if (MatchPeek(TokenType.Keyword, "fun"))
            {
                Match(TokenType.Keyword, "fun");
                Match(TokenType.Symbol, "(");
                List<SecRLabel> args = ParseSecRLabels();
                Match(TokenType.Symbol, ")");
                Match(TokenType.Symbol, "->");
                SecLabel ret = ParseSecLabel();
                return new SecFun(args, ret);
            }
            else if (MatchPeek(TokenType.Symbol, "[") || CheckType(TokenType.Variable))
                return ParseSecBase();
            else
                Error("Invalid security label");
            return null;
        }

        public SecRLabel ParseSecRLabel()
        {
            if (MatchPeek(TokenType.Symbol, "{"))
            {
                Match(TokenType.Symbol, "{");
                List<SecRLabel> labels = ParseSecRLabels();
                Match(TokenType.Symbol, "}");
                return new SecRTuple(labels);
            }
            else if (MatchPeek(TokenType.Symbol, "("))
            {
                Match(TokenType.Symbol, "(");
                SecRLabel label = ParseSecRLabel();
                Match(TokenType.Symbol, ")");
                return label;
            }
            else if (MatchPeek(TokenType.Keyword, "fun"))
            {
                Match(TokenType.Keyword, "fun");
                Match(TokenType.Symbol, "(");
                List<SecRLabel> args = ParseSecRLabels();
                Match(TokenType.Symbol, ")");
                Match(TokenType.Symbol, "->");
                SecRLabel ret = ParseSecRLabel();
                return new SecRFun(args, ret);
            }
            else if (CheckType(TokenType.Variable))
            {
                return ParseSecVar();
            }
            else
            {
                Error("Invalid restricted security label");
            }
            return null;
        }

        public SecBase ParseSecBase()
        {
            List<SecPolicy> policies = new List<SecPolicy>();
            List<SecVar> secvs = new List<SecVar>();
            if (MatchPeek(TokenType.Symbol, "["))
            {
                Match(TokenType.Symbol, "[");
                if (MatchPeek(TokenType.Symbol, "]"))
                {
                    Match(TokenType.Symbol, "]");
                    return new SecBase(null, null);
                }
                policies = ParseSecPolicies();
                Match(TokenType.Symbol, "]");
                return new SecBase(new List<SecVar>(), policies);
            }
            secvs = ParseSecVars();
            if (MatchPeek(TokenType.Symbol, "+"))
            {
                Match(TokenType.Symbol, "+");
                Match(TokenType.Symbol, "[");
                policies = ParseSecPolicies();
                Match(TokenType.Symbol, "]");
            }
            return new SecBase(secvs, policies);
        }

        public List<SecRLabel> ParseSecRLabels()
        {
            List<SecRLabel> labels = new List<SecRLabel>();
            labels.Add(ParseSecRLabel());
            while (MatchPeek(TokenType.Symbol, ","))
            {
                Chomp();
                labels.Add(ParseSecRLabel());
            }
            return labels;
        }

        public SecVar ParseSecVar()
        {
            if (CheckType(TokenType.Variable))
            {
                SecVar v = new SecVar(CurrentToken().Value);
                Chomp();
                return v;
            }
            else
            {
                Error("Expected a variable");
            }
            return null;
        }

        public List<SecVar> ParseSecVars()
        {
            List<SecVar> vars = new List<SecVar>();
            vars.Add(ParseSecVar());
            while (MatchPeek(TokenType.Symbol, "+"))
            {
                Chomp();
                if (CheckType(TokenType.Variable))
                    vars.Add(ParseSecVar());
                else
                {
                    UnChomp();
                    break;
                }
            }
            return vars;
        }

        public ISecPid ParseSecPid()
        {
            if (CheckType(TokenType.Atom))
            {
                Token t = CurrentToken();
                PidExpr p = new PidExpr(t.Value, t.Line, t.Col);
                Chomp();
                return p;
            }
            else if (CheckType(TokenType.Variable))
            {
                PidVar p = new PidVar(CurrentToken().Value);
                Chomp();
                return p;
            }
            else
            {
                Error("Expected a pid or pid variable");
            }
            return null;
        }

        public List<ISecPid> ParseSecPids()
        {
            List<ISecPid> sps = new List<ISecPid>();
            sps.Add(ParseSecPid());
            while (MatchPeek(TokenType.Symbol, ","))
            {
                Chomp();
                sps.Add(ParseSecPid());
            }
            return sps;
        }

        public List<SecPolicy> ParseSecPolicies()
        {
            List<SecPolicy> sps = new List<SecPolicy>();
            sps.Add(ParseSecPolicy());
            while (MatchPeek(TokenType.Symbol, ","))
            {
                Chomp();
                sps.Add(ParseSecPolicy());
            }
            return sps;
        }

        public SecPolicy ParseSecPolicy()
        {
            Match(TokenType.Symbol, "{");
            ISecPid o = ParseSecPid();
            Match(TokenType.Symbol, ":");
            List<ISecPid> r = ParseSecPids();
            Match(TokenType.Symbol, "}");
            return new SecPolicy(o, r);
        }
    }
}
